package cn.duyo.spring.context.support;

import cn.duyo.spring.beans.BeansException;
import cn.duyo.spring.beans.factory.config.BeanPostProcessor;
import cn.duyo.spring.context.ApplicationContext;
import cn.duyo.spring.context.ApplicationContextAware;
import cn.duyo.spring.context.ApplicationEventPublisherAware;
import cn.duyo.spring.context.ResourceLoaderAware;

/**
 * 上下文感知包装到一个后置处理器 (这样就能在创建bean时拿到上下文)
 * tips: 由于 ApplicationContext 的获取并不能直接在创建 Bean 时候就可以拿到，所以需要在 refresh 操作时，
 * 把 ApplicationContext 写入到一个包装的 BeanPostProcessor 中去，再由 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization 方法调用。
 *
 * 其他Aware感知接口的数据基本都能在创建bean时得到并设置回去，但是 ApplicationContext 在接口设计上和创建bean时的support包并无直接联系，
 * 只能"曲线救国"的方式，将上下文包装到 BeanPostProcessor。所以需要在 AbstractApplicationContext#refresh 时，把这个后置处理器给设置到 BeanFactory里
 *
 * @author du
 */
public class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        /**
         * TODO!!! 注意这里将包装为一个后缀处理器来执行上下文的 Aware 钩子
         */
        if (bean instanceof ApplicationContextAware){
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }

        /**
         * 由于上下文顶级接口 ApplicationContext 继承了 ApplicationEventPublisher 接口，
         * 所以容器上下文对象拥有 publishEvent 方法，其内部本质是在调用事件广播器的 multicastEvent 方法。
         *
         * TODO 通过实现接口 ApplicationEventPublisherAware，就可以得到 ApplicationContext 对象实例(自身实现了 ApplicationEventPublisherAware 接口，所以有publishEvent 方法)
         *  我们可以直接注入 ApplicationEventPublisher 而不需要实现发布者感知接口。这并非由于 ApplicationContext 可以直接注入，因为上下文并非一个常规Bean，
         */
        if (bean instanceof ApplicationEventPublisherAware){
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }

        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }


        // TODO 还有其他三个Aware，此处并未实现，但逻辑是一样的
        /*if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }

        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }*/
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}